const crypto = require('crypto');

const dayjs = require('dayjs');
const https = require('https');

const qs = require('qs');

const { AccessToken } = require('../models');

const path = require('path');
const fs = require('fs');

class Weapp {
	static injector(ctn) {
		return new Map([
			['app', ctn.make('varal.app')]
		])
	}

	constructor(options) {
		const { app_id, app_secret } = require('../config/config').weapp;
		this.options = Object.assign({
			app_id,
			app_secret
		}, options);

		this.accessToken().then((data) => {
			this.access_token = data;
		}).catch((err) => {

		});
	}

	accessToken() {
		return new Promise(async (resolve, reject) => {
			const { app_id, app_secret } = this.options;

			const { id, access_token, expires_time } = await AccessToken.findOne({
				where: { app_id }
			}) || {}

			if (!access_token || dayjs(expires_time).unix() < dayjs().unix()) {
				const url = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${app_id}&secret=${app_secret}`;
				const data = await this.https(url);

				if (!data.errcode) {
					if (id) {
						await AccessToken.update({
							expires_time: dayjs.unix(dayjs().unix() + data.expires_in).format('YYYY-MM-DD HH:mm:00'),
							access_token: data.access_token
						}, {
							where: {
								app_id
							}
						})
					} else {
						/*
						await AccessToken.destroy({
							where: {
								app_id
							},
							force: true
						});
						*/
						await AccessToken.create({
							app_id,
							expires_time: dayjs.unix(dayjs().unix() + data.expires_in).format('YYYY-MM-DD HH:mm:00'),
							access_token: data.access_token
						})
					}
					resolve(data.access_token);
				} else {
					reject(data.errmsg);
				}
			} else {
				resolve(access_token);
			}
		})
	}

	https(url, data = {}, method = 'get', contentType = 'application/json', dataType = 'json') {
		return new Promise(async (resolve, reject) => {
			const query = qs.parse(require('url').parse(url).query);
			if (!query.grant_type && !query.access_token && !url.includes('map.qq.com')) {
				const access_token = await this.accessToken().catch(() => '');
				url += `${Object.keys(query).length > 0 ? '&' : '?'}access_token=${access_token}`;
			}

			const callback = (res) => {
				const chunks = [];
				res.on('data', data => {
					chunks.push(data);
				});
				res.on('end', err => {
					let data;

					if (dataType === 'json') {
						data = JSON.parse(Buffer.concat(chunks).toString('utf-8'));
					} else {
						data = Buffer.concat(chunks);
					}

					resolve(data);
				});
			}

			if (method === 'get') {
				https.get(url, callback).on('error', (err) => {
				});
			} else if (method === 'post') {
				let req;

				if (contentType == 'multipart/form-data') {
					//获取文件名
					const key = Object.keys(data)[0];
					const ext = path.extname(data[key]);
					const mime = {
						'.png': 'image/png',
						'.gif': 'image/gif',
						'.jpg': 'image/jpeg',
						'.jpeg': 'image/jpeg',
						'.txt': 'text/plain'
					}[ext];

					const boundary = (Math.random() * 9007199254740992).toString(36);

					//https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2
					let payload = `--${boundary}\r\n`;

					/*
					payload += `Content-Disposition: form-data; name="input"\r\n\r\n`;
					payload += `It works!\r\n--${boundary}\r\n`;
					*/

					payload += `Content-Disposition: form-data; name="${key}"; filename="${path.basename(data[key])}"\r\n`;
					payload += `Content-Type: ${mime}\r\n`;
					payload += `Content-Transfer-Encoding: binary\r\n\r\n`;

					payload = Buffer.concat([
						Buffer.from(payload),
						fs.readFileSync(data[key]),
						Buffer.from(`\r\n--${boundary}--\r\n`)
					]);

					req = https.request(url, {
						method: 'post',
						headers: {
							'Content-Type': `multipart/form-data; boundary=${boundary}`,
							'Content-Length': Buffer.byteLength(payload)
						}
					}, callback);

					req.write(payload);
				} else {
					try {
						data = JSON.stringify(data);
					} catch (err) {
						data = '';
					}

					req = https.request(url, {
						method: 'post',
						headers: {
							'Content-Type': 'application/json',
							'Content-Length': Buffer.byteLength(data)
						}
					}, callback);

					req.write(data);
				}

				req.on('error', (err) => {

				});

				req.end();
			}
		});
	}

	checkSignature(timestamp, nonce, signature) {
		const { token } = this.options;

		var s = [token, timestamp, nonce].sort().join('');

		return signature === crypto.createHash('sha1').update(s).digest('hex');
	}

	snsSession(code) {
		const { app_id, app_secret } = this.options;

		return new Promise(async (resolve, reject) => {
			const res = await this.https(`https://api.weixin.qq.com/sns/jscode2session?appid=${app_id}&secret=${app_secret}&js_code=${code}&grant_type=authorization_code`);
			resolve(res);
		});
	}

	userPhone(code) {
		return new Promise(async (resolve, reject) => {
			const res = await this.https(`https://api.weixin.qq.com/wxa/business/getuserphonenumber`, { code }, 'post');
			resolve(res);
		});
	}

	qrcode(scene, page, width = 430) {
		const file_dir = path.join(__dirname, '../public/uploadfile/qrcode');

		return new Promise(async (resolve, reject) => {
			fs.stat(file_dir, (err, data) => {
				if (err) fs.mkdirSync(file_dir);

				const file_path = path.join(file_dir, `${scene}.jpg`);
				//判断文件是否存在
				fs.stat(file_path, async (err, data) => {
					if (err) {
						//创建文件
						this.https('https://api.weixin.qq.com/wxa/getwxacodeunlimit', {
							scene,
							page,
							width
						}, 'post', 'application/json', 'buffer').then((data) => {
							if (data) fs.writeFileSync(file_path, data);

							resolve(`${scene}.jpg`);
						});
					} else {
						resolve(`${scene}.jpg`);
					}
				});
			});
		});
	}

	urlscheme(path, query, expires_in = 0) {
		const expire_time = (expires_in ? dayjs().add(expires_in, 'second') : dayjs().add(30, 'day')).unix();

		return new Promise(async (resolve, reject) => {
			const res = await this.https(`https://api.weixin.qq.com/wxa/generate_urllink`, {
				path,
				query,
				expire_time
			}, 'post');
			resolve(res);
		});
	}
}
module.exports = Weapp;
